<?php

namespace yunj\library\throttle;

use yunj\library\traits\Redis;

abstract class Throttle {

    use Redis;

    /**
     * 时间区间缩写映射值
     */
    const DURATION_VALS = [
        's' => 1,
        'm' => 60,
        'h' => 3600,
        'd' => 86400,
    ];

    /**
     * @var \Redis
     */
    private $redis;

    /**
     * 当前请求标识
     * @var string
     */
    protected $key = '';

    /**
     * 单位时间区间限制次数
     * @var int
     */
    protected $limit;

    /**
     * 单位时间区间
     * @var int
     */
    protected $duration;

    /**
     * 等待时间（秒）
     * @var int
     */
    protected $waitSeconds = 0;

    /**
     * @return int
     */
    public function getLimit(): int {
        return $this->limit;
    }

    /**
     * @return int
     */
    public function getDuration(): int {
        return $this->duration;
    }

    /**
     * @return string
     */
    public function getKey(): string {
        if ($this->key) return $this->key;
        $this->key = 'yunj.library.throttle:' . static::getRequestKey();
        return $this->key;
    }

    /**
     * @param string $key
     * @return $this
     */
    public function setKey(string $key) {
        $this->key = $key;
        return $this;
    }

    /**
     * 获取等待时间（秒）
     * @return int
     */
    public function getWaitSeconds(): int {
        return $this->waitSeconds;
    }

    /**
     * Throttle constructor.
     * @param string $visitRate 单位时间(/秒)区间限制次数。
     * 如：1分钟100次
     * 写法1：100/m
     * 写法2：100/60
     * @param string $key 当前请求标识
     */
    public function __construct(string $visitRate = '', string $key = '') {
        // 设置访问频率
        [$this->limit, $this->duration] = $this->parseVisitRate($visitRate);
        $this->setKey($key);
    }

    /**
     * 解析频率配置项
     * @param string $visitRate
     * @return int[]
     */
    protected function parseVisitRate(string $visitRate): array {
        if (!$visitRate || !strstr($visitRate, '/')) throw new \RuntimeException('访问频率格式错误');
        [$limit, $duration] = explode("/", $visitRate);
        $limit = (int)$limit;
        $duration = static::DURATION_VALS[$duration] ?? (int)$duration;
        if (!$limit || !$duration) throw new \RuntimeException('访问频率错误');
        return [$limit, $duration];
    }

    /**
     * 限流判断
     * @return bool
     */
    public abstract function check(): bool;

    /**
     * 获取请求key
     * @return string
     */
    public static function getRequestKey(): string {
        static $key;
        if ($key) return $key;
        $url = request_url();
        $url = strpos($url, '.') ? strstr($url, '.', true) : $url;
        $key = md5($url . request_ip());
        return $key;
    }
}